package service

import (
	"fmt"
	"sort"
	"strings"
	"time"

	"file_explorer/common/consts"
	"file_explorer/common/log"
	"file_explorer/common/util"
	"file_explorer/model"
)

// 指定目录，填充文件元数据
func fillFileMetadata(dir string, fileInfos []*model.FileInfo, option *model.QueryOption) []*model.FileInfo {
	metadata, err := ReadLatestMetadata(dir)
	if err != nil {
		log.Error("read metadata failed, dir=%v, err=%v", dir, err)
		return fileInfos
	}

	if metadata == nil || metadata.FilenameMap == nil {
		log.Debug("no metadata, dir=%v", dir)
		return fileInfos
	}

	filledMetadata := make(map[string]struct{})
	for _, fileInfo := range fileInfos {
		if fileMetadata, ok := metadata.FilenameMap[fileInfo.Name]; ok {
			fileInfo.Metadata = fileMetadata
			filledMetadata[fileInfo.Name] = struct{}{}
		}
	}

	if option.GetShowVirtualFile() {
		// 显示文件不存在、包含元数据的条目
		for name, fileMetadata := range metadata.FilenameMap {
			if _, ok := filledMetadata[name]; !ok {
				fileInfos = append(fileInfos, &model.FileInfo{
					Dir:        dir,
					Name:       name,
					Type:       consts.FileTypeVirtual,
					ModifyTime: metadata.MetaFileInfo.ModifyTime,
					Metadata:   fileMetadata,
				})
			}
		}
	}

	log.Debug("fill metadata success, dir=%v", dir)

	return fileInfos
}

// 获取指定目录下的元文件列表
func GetMetaFiles(dir string) []*model.FileInfo {
	metaDir := util.PathJoin(dir, consts.MetadataDirName)

	rawMetaFiles, err := readDir(metaDir)
	if err != nil {
		log.Debug("no meta files, dir=%v, err=%v", dir, err)
		return nil
	}

	metaFiles := make([]*model.FileInfo, 0, len(rawMetaFiles))
	for _, metaFile := range rawMetaFiles {
		prefix, _ := util.SplitFilename(consts.MetaFilename)
		if strings.HasPrefix(metaFile.Name, prefix) {
			metaFiles = append(metaFiles, metaFile)
		}
	}

	if len(metaFiles) == 0 {
		return nil
	}

	// 按修改时间倒序排列
	sort.Slice(metaFiles, func(i, j int) bool {
		return metaFiles[i].Name > metaFiles[j].Name
	})

	log.Debug("get meta files success, dir=%v, metaFileNum=%v", dir, len(metaFiles))

	return metaFiles
}

// 指定目录，读取最新的元数据
func ReadLatestMetadata(dir string) (*model.FileMetadataMap, error) {
	// 获取最新的元文件
	metaFiles := GetMetaFiles(dir)
	if len(metaFiles) == 0 {
		return nil, nil
	}

	// 读取元数据
	metadata, err := ReadMetadata(metaFiles[0])
	if err != nil {
		return nil, nil
	}

	log.Debug("got latest meta data, dir=%v, metaFile=%v", dir, metadata.MetaFileInfo)

	return metadata, nil
}

// 指定绝对路径，读取元数据
func ReadMetadata(metaFile *model.FileInfo) (*model.FileMetadataMap, error) {
	metadata := &model.FileMetadataMap{
		MetaFileInfo: metaFile,
	}

	err := util.ReadYmlFile(metaFile.GetPath(), metadata)
	if err != nil {
		log.Error("read metadata meta file failed, err=%v", err)
		return nil, err
	}

	log.Debug("read metadata success, metaFile=%v", metaFile.GetPath())

	return metadata, nil
}

// 指定目录，写入元数据
func CreateMetadata(dir string, metadata *model.FileMetadataMap) (string, error) {
	// meta目录不存在时创建
	metaDirPath := util.PathJoin(dir, consts.MetadataDirName)
	if !util.PathExists(metaDirPath) {
		if err := util.MkdirIfNotExist(metaDirPath); err != nil {
			log.Error("create meta file dir failed, path=%v, err=%v", metaDirPath, err)
			return "", err
		}
	}

	filename := util.AddSuffix(consts.MetaFilename, fmt.Sprintf(".%s", time.Now().Format(consts.TimeLayoutWithoutMark)))

	err := util.WriteYmlFile(metadata, util.PathJoin(metaDirPath, filename))
	if err != nil {
		log.Error("create metadata failed, dir=%v, err=%v", dir, err)
		return "", err
	}

	log.Debug("create metadata success, dir=%v", dir)

	return filename, nil
}

func UpdateLatestMetadata(fileInfo *model.FileInfo) (string, error) {
	// 获取最新元数据
	metadata, err := ReadLatestMetadata(fileInfo.Dir)
	if err != nil {
		return "", err
	}

	// 新增元文件
	if metadata == nil {
		return CreateMetadata(fileInfo.Dir, &model.FileMetadataMap{
			FilenameMap: map[string]*model.FileMetadata{
				fileInfo.Name: fileInfo.Metadata,
			},
		})
	}

	// 更新元文件
	metadata.SetFileMetadata(fileInfo)
	err = util.WriteYmlFile(metadata, metadata.MetaFileInfo.GetPath())
	if err != nil {
		log.Error("update metadata failed, meta_file=%v, err=%v", metadata.MetaFileInfo.GetPath(), err)
		return "", err
	}

	log.Debug("update metadata success, meta_file=%v", metadata.MetaFileInfo.GetPath())

	return metadata.MetaFileInfo.Name, nil
}
